<?php
/**
 * 支付公用类
 */

namespace app\services;


use app\controller\v1\Pay;
use app\model\Change;
use app\model\CommodityOrder;
use app\model\ShopOrderModel as Order;
use app\model\User;
use exceptions\ApiException;
use think\Exception;
use think\exception\HttpException;
use think\facade\Db;
use think\Response;

abstract class BasePayServices
{

    /**
     * 支付状态
     * @var bool
     */
    protected $pay_status = false;

    /**
     * 支付回调信息
     * @var string
     */
    protected $notify_info = '';

    /**
     * 支付通道 1 支付宝 2 微信 3 余额 4 线下
     * @var int
     */
    protected $pay_way = 1;

    /**
     * 支付配置
     * @param  $userId
     * @param  $data
     * @param  $type
     * @return array
     */
    public function _payConf($userId, $data, $type = 1)
    {
        $pay_way = get_config(2, 'PAYTYPE');
        if ($data->data['channel'] == 4 && !isset($pay_way['offline']))  // 线下支付
            new HttpException(500, '当前支付通道未开启，下单失败');
        if ($data->data['channel'] == 1 && !isset($pay_way['online']['zhifubao'])) // 支付宝
            new HttpException(500, '当前支付通道未开启，下单失败');
        if ($data->data['channel'] == 2 && !isset($pay_way['online']['wechat'])) // 微信
            new HttpException(500, '当前支付通道未开启，下单失败');
        if ($data->data['channel'] == 3 && !isset($pay_way['online']['balance'])) // 余额
            new HttpException(500, '当前支付通道未开启，下单失败');
//        $user_obj = Db::name('user')->where('id', $uid);
//        if ($user_obj->value('forbid_pay_at', 0) >= time()) {
//             new HttpException(500,'下单频繁，禁止24小时内再次下单');
//        } else {
//            $today_no_pay = Db::name('payment')->where('uid', $uid)->whereTime('creat_at', 'today')->where('state', 7)->count('id'); // 未支付订单数量
//            if ($today_no_pay >= 5) { // 今天超过5单未支付，则24小时不允许下单
//                $user_obj->update(['forbid_pay_at' => time() + 86400]);
//                 new HttpException(500,'下单频繁，禁止24小时内再次下单');
//            }
//        }
        $body = $total_amount = $remarks = '';
        switch ($data->data['order_type']) {
            case 1: // 充值
                $body = '充值';
                $total_amount = $data->data['money'];
                break;
            case 2: // 购买599代理
                $body = '购买会员';
                $total_amount = '699.00';
                break;
            case 4: // 购买2980代理
                $body = '购买会员';
                $total_amount = '2980.00';
                break;
            case 3: //购买商品
                //下单
                $result = app()->make(CommodityOrder::class)->Pay($data, 2);
                if ($result['total_price'] != $data->data['money']) {
                    new HttpException(500, '下单失败！1');
                }
                $order_sn = implode(',', $result['order_id']);
                $body = '购买商品';
                $total_amount = $result['total_price'];
                $remarks = $order_sn;
//                //更改订单支付状态为线下支付待审核状态
                if ($data['channel'] == 3) {
//                    Order::where('order_sn', 'in', $order_sn)->save(['pay_type' => 4, 'status' => 4]);
                }
                break;
        }
        if ($total_amount != $data->data['money']) {
            new HttpException(500, '下单失败！1');
        }
        if ($data['channel'] == 4 && (!isset($data['voucher_img']) || empty($data['voucher_img'])))
            new HttpException(500, '请上传支付凭证');
        if (!$body || !$total_amount || $total_amount <= 0)
            new HttpException(500, '配置错误');
        return [$body, $type == 1 ? $total_amount : bcmul($total_amount, 100, 2), $data['order_type'], $remarks, $data['trade_type'], $data['channel'], isset($data['voucher_img']) ? $data['voucher_img'] : ''];
    }

    /**
     * 保存订单信息 (线上)
     * @param array $param
     * @param int $pay_way
     */
    public function setPaymentOrder(array $param, int $pay_way = 1)
    {
        $insert = [
            'total_fee' => $pay_way == 1 ? $param['total_amount'] : $param['total_amount'] / 100,
            'trade_type' => $param['trade_type'],
            'body' => $param['body'],
            'state' => 7,
            'out_trade_no' => $param['out_trade_no'],
            'pay_way' => $param['pay_way'],
            'remarks' => $param['remarks'],
            'order_type' => $param['order_type'],
            'user_id' => $param['userId'],
            'voucher_img' => $param['voucher_img'],
            'prepay_id' => isset($param['prepay_id']) ? $param['prepay_id'] : '',
            'offline_pay_way' => isset($param['offline_pay_way']) ? $param['offline_pay_way'] : 0,
        ];
        Db::name('payment')->insert($insert);
    }

    /**
     * 保存订单信息（线下）
     * @param array $param
     * @param int $pay_way
     * @throws \Exception
     */
    public function setPaymentOrderOffline(array $param, int $pay_way = 1)
    {
        $insert = [
            'total_fee' => $pay_way == 1 ? $param['total_amount'] : $param['total_amount'] / 100,
            'trade_type' => $param['trade_type'],
            'body' => $param['body'],
            'state' => 7,
            'out_trade_no' => $param['out_trade_no'],
            'pay_way' => $param['pay_way'],
            'remarks' => $param['remarks'],
            'order_type' => $param['order_type'],
            'user_id' => $param['userId'],
            'voucher_img' => $param['voucher_img'],
            'prepay_id' => Db::name('pay_config')->where(['channel' => 1, 'is_offline' => 1])->value('mchid'),
            'offline_pay_way' => isset($param['offline_pay_way']) ? $param['offline_pay_way'] : 0,
            'trade_no' => isset($param['trade_no']) ? $param['trade_no'] : 0,
        ];

        Db::startTrans();
        try {
            Db::name('payment')->insert($insert);
            /** @var Pay $pay */
            $pay = app()->make(Pay::class);
            if ($param['offline_pay_way'] > 0) {
                $result = $pay->tradeOrderStatus((string)($param['trade_no']), 4, $param['offline_pay_way']);
                if ($result) {
                    $this->notify_info = $insert;
                    $this->afterPayOffline($param['offline_pay_way']);
                } else {
                    new HttpException(500, '未查询到该订单，请确认提交的订单号是否有误');
                }
            }
            Db::commit();
        } catch (\Exception $exception) {
            Db::rollback();
            new HttpException(500, $exception->getMessage());
        }
    }

    /**
     * 更改订单状态（线下）
     * @param int $offline_pay_way
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function afterPayOffline(int $offline_pay_way)
    {
        $where['pay_way'] = 4;
        if ($offline_pay_way == 1) {
            $where['trade_no'] = $this->notify_info['trade_no'];
        } else {
            $where['trade_no'] = $this->notify_info['transaction_id'];
        }
        $pay_info = Db::name('payment')->where($where)->findOrFail();
        switch ($pay_info['order_type']) {
            case 1: // 充值
                set_user_money($pay_info['user_id'], $pay_info['total_fee'], 1);
                break;
            case 3: // 充值会员
                $user = Db::name('user')->where('id', $pay_info['user_id'])->field('vip,vip_expire_time,pid,path')->find();
                $vip_config = get_vip_config($pay_info['remarks']);
                if (!$user['vip_expire_time'] || strtotime($user['vip_expire_time']) < time())
                    $user['vip_expire_time'] = date('Y-m-d H:i:s', time());
                $date = new \DateTime($user['vip_expire_time']);
                $date->add(new \DateInterval(get_vip_datetime_string($vip_config['type'])['time']));
                $vip_expire_time = $date->format('Y-m-d H:i:s'); // vip到期时间
                $user_update['vip_expire_time'] = $vip_expire_time;
                $user_update['vip_type'] = $vip_config['type'];
                $user_update['vip'] = get_vip_datetime_string($vip_config['type'])['vip_type'];
                //更新用户vip信息
                Db::name('User')->where('id', $pay_info['user_id'])->update($user_update);
//                Db::name('vip_log')->insert(['uid' => $pay_info['user_id'], 'type' => 1, 'pay_price' => $pay_info['total_fee'], 'vip_expire_time' => $vip_expire_time, 'vip_type' => $pay_info['remarks']]);
                if ($user['pid']) { // 购买会员卡，上级返10% 上上级返10% 上上上级返5%
                    $path = explode(',', $user['path']);
                    if ($path[0] == 0)
                        unset($path[0]);
                    $path = array_reverse($path, false);
                    $path = array_slice($path, 0, 3);
                    $config_arr = [0 => 0.1, 1 => 0.1, 2 => 0.05];
                    foreach ($path as $key => $value) {
                        $return_money = $pay_info['total_fee'] * $config_arr[$key];
                        set_user_money($value, $return_money, 6, 1, $pay_info['user_id']);
                    }
                }
                break;
            case 4: //购买商品
                $order = Order::where(['order_sn' => explode(',', $pay_info['remarks'])])->select()->toArray();
                Order::payDoneOrderCallback($order, $pay_info['pay_way'], $pay_info['user_id']);
                break;
        }
    }


    /**
     * 更改订单状态
     * @return mixed
     */
    public function afterPay()
    {
        if ($this->pay_status && !empty($this->notify_info)) {
            $pay_info = Db::name('payment')->where(['out_trade_no' => $this->notify_info['out_trade_no'], 'state' => 7, 'pay_way' => $this->pay_way])->findOrEmpty();
            if (!empty($pay_info)) {
                Db::startTrans();
                try {
                    $trade_no = '';
                    if ($this->pay_way == 1) {
                        $trade_no = $this->notify_info['trade_no'];
                    } elseif ($this->pay_way == 2) {
                        $trade_no = $this->notify_info['transaction_id'];
                    }
                    RedisLock('notifyPay' . $trade_no, 5);
                    Db::name('payment')->where('id', $pay_info['id'])->update(['state' => 6, 'pay_at' => date('Y-m-d H:i:s'), 'out_trade_no' => $trade_no]); // 更改订单状态
                    switch ($pay_info['order_type']) {
                        case 1: // 充值
                            $user = User::lock(true)->where('id', $pay_info['user_id'])->findOrFail();
                            Change::SetUserMoney($user->id, $user->money, $pay_info['total_fee'], 2);
                            $user->money += $pay_info['total_fee'];
                            $user->save();
                            break;
                        case 2: // 充值599会员
                            $user = User::lock(true)->where('id', $pay_info['user_id'])->findOrFail();
                            $user->vip = 1;
                            $user->vip_time = date('Y-m-d H:i:s', strtotime("+1 year"));
                            $user->save();
                            break;
                        case 4: // 充值2980会员
                            $user = User::lock(true)->where('id', $pay_info['user_id'])->findOrFail();
                            $user->vip = 2;
                            $user->vip_time = date('Y-m-d H:i:s', strtotime("+1 year"));
                            $user->save();
                            break;
                        case 3: //购买商品
                            CommodityOrder::payDoneOrderCallback($pay_info['user_id'], $pay_info['remarks']);
                            break;
                    }
                    Db::commit();
                    if ($this->pay_way == 1) {
                        echo 'success';
                        die;
                    } elseif ($this->pay_way == 2) {
                        return Response::create(['code' => 'SUCCESS', 'message' => '成功'], 'json');
                    } else {
                        return app('json')->json_success('success');
                    }

                } catch (\Exception $e) {
                    Db::rollback();
                }
            }
        }
        if ($this->pay_way == 1) {
            echo 'failure';
            die;
        } elseif ($this->pay_way == 2) {
            return Response::create(['code' => 'ERROR', 'message' => 'error'], 'json');
        } else {
            return app('json')->json_error('error');
        }
    }
}